
(1)
#$ find . | grep "R$" | xargs grep -n "FindVariableFeatures" --color=auto
#./seurat-4.1.0/R/generics.R:204:FindVariableFeatures <- function(object, ...) {


#' Find variable features
#'
#' Identifies features that are outliers on a 'mean variability plot'.
#'
#' For the mean.var.plot method:
#' Exact parameter settings may vary empirically from dataset to dataset, and
#' based on visual inspection of the plot. Setting the y.cutoff parameter to 2
#' identifies features that are more than two standard deviations away from the
#' average dispersion within a bin. The default X-axis function is the mean
#' expression level, and for Y-axis it is the log(Variance/mean). All mean/variance
#' calculations are not performed in log-space, but the results are reported in
#' log-space - see relevant functions for exact details.
#'
#' @param object An object
#' @param ... Arguments passed to other methods
#'
#' @rdname FindVariableFeatures
#' @export FindVariableFeatures
#'
#' @aliases FindVariableGenes
#'
FindVariableFeatures <- function(object, ...) {
  UseMethod(generic = 'FindVariableFeatures', object = object)
}



()
#./seurat-4.1.0/R/preprocessing.R:2115:FindVariableFeatures.Seurat <- function(

#' @param assay Assay to use
#'
#' @rdname FindVariableFeatures
#' @concept preprocessing
#' @export
#' @method FindVariableFeatures Seurat
#'
FindVariableFeatures.Seurat <- function(
  object,
  assay = NULL,
  selection.method = "vst",
  loess.span = 0.3,
  clip.max = 'auto',
  mean.function = FastExpMean,
  dispersion.function = FastLogVMR,
  num.bin = 20,
  binning.method = "equal_width",
  nfeatures = 2000,
  mean.cutoff = c(0.1, 8),
  dispersion.cutoff = c(1, Inf),
  verbose = TRUE,
  ...
) {
  # 默认实验
  assay <- assay %||% DefaultAssay(object = object)

  # 获取 该 Assay 对象
  assay.data <- GetAssay(object = object, assay = assay)

  # 扔给Assay对象的函数
  assay.data <- FindVariableFeatures(
    object = assay.data,
    selection.method = selection.method,
    loess.span = loess.span,
    clip.max = clip.max,
    mean.function = mean.function,
    dispersion.function = dispersion.function,
    num.bin = num.bin,
    binning.method = binning.method,
    nfeatures = nfeatures,
    mean.cutoff = mean.cutoff,
    dispersion.cutoff = dispersion.cutoff,
    verbose = verbose,
    ...
  )

  # 覆盖回 原实验
  object[[assay]] <- assay.data

  # 如果实验是 SCTAssay 对象
  if (inherits(x = object[[assay]], what = "SCTAssay")) {
  	# 获取残差，先跳过。
    object <- GetResidual(
      object = object,
      assay = assay,
      features = VariableFeatures(object = assay.data),
      verbose = FALSE
    )
  }

  # 记录日志
  object <- LogSeuratCommand(object = object)
  return(object)
}







()
#./seurat-4.1.0/R/preprocessing.R:2014:FindVariableFeatures.Assay <- function(


#' @param nfeatures Number of features to select as top variable features;
#' only used when \code{selection.method} is set to \code{'dispersion'} or
#' \code{'vst'} #只有这2个模式时参数 nfeatures有效。
#' @param mean.cutoff A two-length numeric vector with low- and high-cutoffs for
#' feature means
#' @param dispersion.cutoff A two-length numeric vector with low- and high-cutoffs for
#' feature dispersions
#'
#' @rdname FindVariableFeatures
#' @concept preprocessing
#'
#' @importFrom utils head
#' @export
#' @method FindVariableFeatures Assay
#'
FindVariableFeatures.Assay <- function(
  object,
  selection.method = "vst",
  loess.span = 0.3,
  clip.max = 'auto',
  mean.function = FastExpMean,
  dispersion.function = FastLogVMR,
  num.bin = 20,
  binning.method = "equal_width",
  nfeatures = 2000,
  mean.cutoff = c(0.1, 8),
  dispersion.cutoff = c(1, Inf),
  verbose = TRUE,
  ...
) {
  # (A1) 如果2个范围参数长度不是2，则报错
  if (length(x = mean.cutoff) != 2 || length(x = dispersion.cutoff) != 2) {
    stop("Both 'mean.cutoff' and 'dispersion.cutoff' must be two numbers")
  }

  # (A2) 如果是vst，则使用 assay@counts 数据
  if (selection.method == "vst") {
    data <- GetAssayData(object = object, slot = "counts") #返回的是数据框
    # if (ncol(x = data) < 1 || nrow(x = data) < 1) {
    # 如果 counts 为空，则警告，并使用 data
    if (IsMatrixEmpty(x = data)) {
      warning("selection.method set to 'vst' but count slot is empty; will use data slot instead")
      data <- GetAssayData(object = object, slot = "data")
    }
  # 否则，使用 assay@data
  } else {
    data <- GetAssayData(object = object, slot = "data")
  }

  # 又传给 matrix/data.frame 类的函数
  hvf.info <- FindVariableFeatures(
    object = data,
    selection.method = selection.method,
    loess.span = loess.span,
    clip.max = clip.max,
    mean.function = mean.function,
    dispersion.function = dispersion.function,
    num.bin = num.bin,
    binning.method = binning.method,
    verbose = verbose,
    ...
  )
  # 赋值，按names()覆盖该内部对象
  object[[names(x = hvf.info)]] <- hvf.info



  # (A3) 保留第一列不等于0的行
  hvf.info <- hvf.info[which(x = hvf.info[, 1, drop = TRUE] != 0), ]

  # 如果是 vst 方法，则按照 vst.variance.standardized 列 降序排列
  if (selection.method == "vst") {
    hvf.info <- hvf.info[order(hvf.info$vst.variance.standardized, decreasing = TRUE), , drop = FALSE]
  # 否则，按照 mvp.dispersion 列 降序排列
  } else {
    hvf.info <- hvf.info[order(hvf.info$mvp.dispersion, decreasing = TRUE), , drop = FALSE]
  }

  # 选择方法：vst 则是默认
  selection.method <- switch(
    EXPR = selection.method,
    'mvp' = 'mean.var.plot',
    'disp' = 'dispersion',
    selection.method
  )

  # 高变基因，筛选方法。
  # switch 等号右侧还可以是 花括号{}围起来的多行代码
  top.features <- switch(
    EXPR = selection.method,
    'mean.var.plot' = {
      means.use <- (hvf.info[, 1] > mean.cutoff[1]) & (hvf.info[, 1] < mean.cutoff[2])
      dispersions.use <- (hvf.info[, 3] > dispersion.cutoff[1]) & (hvf.info[, 3] < dispersion.cutoff[2])
      rownames(x = hvf.info)[which(x = means.use & dispersions.use)]
    },
    'dispersion' = head(x = rownames(x = hvf.info), n = nfeatures),
    'vst' = head(x = rownames(x = hvf.info), n = nfeatures), #取前n行作为高变基因
    stop("Unknown selection method: ", selection.method)
  )

  # 写入函数，写入高变基因 assay@var.features
  VariableFeatures(object = object) <- top.features

  

  # (A4) 添加 assay@meata.features 最后一列
  # 是否等于'vst'?
  vf.name <- ifelse(
    test = selection.method == 'vst',
    yes = 'vst',
    no = 'mvp'
  )
  # 添加后缀 'vst.variable'
  vf.name <- paste0(vf.name, '.variable')
  # 为 pbmc@assays$RNA@meta.features 添加最后一列：是否是 高变基因
  object[[vf.name]] <- rownames(x = object[[]]) %in% top.features

  return(object)
}






()
> `VariableFeatures<-`
function (object, ..., value) 
{
    UseMethod(generic = "VariableFeatures<-", object = object)
}
<bytecode: 0x00000000167167b8>
<environment: namespace:SeuratObject>

# ./seurat-object-4.0.4/R/assay.R:591:"VariableFeatures<-.Assay" <- function(object, ..., value) {
作用就是更新 assay@@var.features 这个字符串 slot。



#' @rdname VariableFeatures
#' @export
#' @method VariableFeatures<- Assay
#'
"VariableFeatures<-.Assay" <- function(object, ..., value) {

  CheckDots(...)
  # 如果右值长度为0, 也即是c()，则更新 assay@@var.features=c()，直接返回
  if (length(x = value) == 0) {
    slot(object = object, name = 'var.features') <- character(length = 0)
    return(object)
  }

  # 如果右值中带有_，则警告并替换为-
  if (any(grepl(pattern = '_', x = value))) {

    warning(
      "Feature names cannot have underscores '_', replacing with dashes '-'",
      call. = FALSE,
      immediate = TRUE
    )
    value <- gsub(pattern = '_', replacement = '-', x = value)
  }

  # 按照基因名是否在assay中，把 gene list 分隔为2个元素的list
  value <- split(x = value, f = value %in% rownames(x = object))

  # 如果有不在assay中的基因
  if (length(x = value[['FALSE']]) > 0) {
    # 如果在assay中的基因长度为0，则报错
    if (length(x = value[['TRUE']]) == 0) {
      stop(
        "None of the features provided are in this Assay object",
        call. = FALSE
      )
    # 如果在assay中的基因长度大于0，则警告: 不在assay中的基因为...
    } else {
      warning(
        "Not all features provided are in this Assay object, removing the following feature(s): ",
        paste(value[['FALSE']], collapse = ', '),
        call. = FALSE,
        immediate. = TRUE
      )
    }
  }
  # 更新 slot，只写入在assay中的基因名
  slot(object = object, name = 'var.features') <- value[['TRUE']]

  return(object)
}









() 核心函数！
#./seurat-4.1.0/R/preprocessing.R:1914:FindVariableFeatures.default <- function(


#' @param selection.method How to choose top variable features. Choose one of :
#' \itemize{
#'   \item{vst:}{ First, fits a line to the relationship of log(variance) and
#'   log(mean) using local polynomial regression (loess). Then standardizes the
#'   feature values using the observed mean and expected variance (given by the
#'   fitted line). Feature variance is then calculated on the standardized values
#'   after clipping to a maximum (see clip.max parameter).}
#'   \item{mean.var.plot (mvp):}{ First, uses a function to calculate average
#'   expression (mean.function) and dispersion (dispersion.function) for each
#'   feature. Next, divides features into num.bin (deafult 20) bins based on
#'   their average expression, and calculates z-scores for dispersion within
#'   each bin. The purpose of this is to identify variable features while
#'   controlling for the strong relationship between variability and average
#'   expression.}
#'   \item{dispersion (disp):}{ selects the genes with the highest dispersion values}
#' }
#' @param loess.span (vst method) Loess span parameter used when fitting the
#' variance-mean relationship
#' @param clip.max (vst method) After standardization values larger than
#' clip.max will be set to clip.max; default is 'auto' which sets this value to
#' the square root of the number of cells
#' @param mean.function Function to compute x-axis value (average expression).
#'  Default is to take the mean of the detected (i.e. non-zero) values
#' @param dispersion.function Function to compute y-axis value (dispersion).
#' Default is to take the standard deviation of all values
#' @param num.bin Total number of bins to use in the scaled analysis (default
#' is 20)
#' @param binning.method Specifies how the bins should be computed. Available
#' methods are:
#' \itemize{
#'   \item{equal_width:}{ each bin is of equal width along the x-axis [default]}
#'   \item{equal_frequency:}{ each bin contains an equal number of features (can
#'   increase statistical power to detect overdispersed features at high
#'   expression values, at the cost of reduced resolution along the x-axis)}
#' }
#' @param verbose show progress bar for calculations
#'
#' @rdname FindVariableFeatures
#' @concept preprocessing
#' @export
#'
FindVariableFeatures.default <- function(
  object,
  selection.method = "vst",
  loess.span = 0.3,
  clip.max = 'auto',
  mean.function = FastExpMean,
  dispersion.function = FastLogVMR,
  num.bin = 20,
  binning.method = "equal_width",
  verbose = TRUE,
  ...
) {
  CheckDots(...)

  #(A1) 如果不是 Matrix，强转
  if (!inherits(x = object, 'Matrix')) {
    object <- as(object = as.matrix(x = object), Class = 'Matrix')
  }

  # 如果不是稀疏矩阵，强转
  if (!inherits(x = object, what = 'dgCMatrix')) {
    object <- as(object = object, Class = 'dgCMatrix')
  }

  #(A2) 如果是 vst
  if (selection.method == "vst") {

    #(B1) 如果 clip.max =='auto'
    if (clip.max == 'auto') {
      # 则自动设置为 列数(细胞数)的开方。
      clip.max <- sqrt(x = ncol(x = object))
    }

    # vst 的直接输入的是 counts，所以直接算的 行平均数，作为基因表达值
    hvf.info <- data.frame(mean = rowMeans(x = object))

    # 这优势一个C++函数，确实是C++控制的进度条的显示
    hvf.info$variance <- SparseRowVar2(
      mat = object,
      mu = hvf.info$mean,
      display_progress = verbose
    )

    # 这2列初始化为0
    hvf.info$variance.expected <- 0
    hvf.info$variance.standardized <- 0

    not.const <- hvf.info$variance > 0

    # 1.使用loess拟合平滑曲线模型
    fit <- loess(
      formula = log10(x = variance) ~ log10(x = mean),
      data = hvf.info[not.const, ],
      span = loess.span
    )
    # 2.获取模型计算的值作为y=var值
    hvf.info$variance.expected[not.const] <- 10 ^ fit$fitted

    # use c function to get variance after feature standardization
    # 3.使用 c 函数计算feature标准化( (counts - mean)/sd )后的方差，注意sd=sqrt(var)
    hvf.info$variance.standardized <- SparseRowVarStd(
      mat = object,
      mu = hvf.info$mean,
      sd = sqrt(hvf.info$variance.expected),
      vmax = clip.max,
      display_progress = verbose
    )
    # 加上 'vst.' 前缀
    colnames(x = hvf.info) <- paste0('vst.', colnames(x = hvf.info))

  #(A2) 如果不是 vst
  # 大概是分bin，跳过
  } else {
    #(B1) 计算 mean 和 dispersion
    if (!inherits(x = mean.function, what = 'function')) {
      stop("'mean.function' must be a function")
    }
    if (!inherits(x = dispersion.function, what = 'function')) {
      stop("'dispersion.function' must be a function")
    }
    feature.mean <- mean.function(object, verbose)
    feature.dispersion <- dispersion.function(object, verbose)
    # 添加基因名
    names(x = feature.mean) <- names(x = feature.dispersion) <- rownames(x = object)
    # 定义na为0
    feature.dispersion[is.na(x = feature.dispersion)] <- 0
    feature.mean[is.na(x = feature.mean)] <- 0

    #(B2) 分bin策略：等宽还是等频率
    data.x.breaks <- switch(
      EXPR = binning.method,
      'equal_width' = num.bin, #按长度分bin
      'equal_frequency' = c(  #按分位数分bin
        quantile(
          x = feature.mean[feature.mean > 0],
          probs = seq.int(from = 0, to = 1, length.out = num.bin)
        )
      ),
      stop("Unknown binning method: ", binning.method)
    )

    #(B3) 把向量 映射为 区间因子
    data.x.bin <- cut(x = feature.mean, breaks = data.x.breaks,
                      include.lowest = TRUE)
    # names 为原来的值
    names(x = data.x.bin) <- names(x = feature.mean)

    #(B4) 计算每个bin内的基因的方差的均值、方差
    mean.y <- tapply(X = feature.dispersion, INDEX = data.x.bin, FUN = mean)
    sd.y <- tapply(X = feature.dispersion, INDEX = data.x.bin, FUN = sd)

    #每个bin内部对 方差 进行 标准化， (x-m)/sd
    feature.dispersion.scaled <- (feature.dispersion - mean.y[as.numeric(x = data.x.bin)]) /
      sd.y[as.numeric(x = data.x.bin)]
    # names 为方差值
    names(x = feature.dispersion.scaled) <- names(x = feature.mean)

    # 合并为df，3列。定义行名、列名。
    hvf.info <- data.frame(feature.mean, feature.dispersion, feature.dispersion.scaled)
    rownames(x = hvf.info) <- rownames(x = object)
    colnames(x = hvf.info) <- paste0('mvp.', c('mean', 'dispersion', 'dispersion.scaled'))
  }

  return(hvf.info)
}















()
# 这两个函数的默认值是用C定义的。
> Seurat:::FastExpMean
function (mat, display_progress) 
{
    .Call("_Seurat_FastExpMean", PACKAGE = "Seurat", 
        mat, display_progress)
}
<bytecode: 0x00000000231e5748>
<environment: namespace:Seurat>



#
#
$ find . | xargs grep -n "FastExpMean" --color=auto 2>/dev/null
./seurat-4.1.0/R/RcppExports.R:48:FastExpMean <- function(mat, display_progress) {

FastExpMean <- function(mat, display_progress) {
    .Call('_Seurat_FastExpMean', PACKAGE = 'Seurat', mat, display_progress)
}



#
# 该函数是这个 cpp 函数暴漏出去的
./seurat-4.1.0/src/RcppExports.cpp:162:// FastExpMean
./seurat-4.1.0/src/RcppExports.cpp:163:Eigen::VectorXd FastExpMean(Eigen::SparseMatrix<double> mat, bool display_progress);
./seurat-4.1.0/src/RcppExports.cpp:164:RcppExport SEXP _Seurat_FastExpMean(SEXP matSEXP, SEXP display_progressSEXP) {
./seurat-4.1.0/src/RcppExports.cpp:169:    rcpp_result_gen = Rcpp::wrap(FastExpMean(mat, display_progress));

// FastExpMean
Eigen::VectorXd FastExpMean(Eigen::SparseMatrix<double> mat, bool display_progress);
RcppExport SEXP _Seurat_FastExpMean(SEXP matSEXP, SEXP display_progressSEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::traits::input_parameter< Eigen::SparseMatrix<double> >::type mat(matSEXP);
    Rcpp::traits::input_parameter< bool >::type display_progress(display_progressSEXP);
    rcpp_result_gen = Rcpp::wrap(FastExpMean(mat, display_progress));
    return rcpp_result_gen;
END_RCPP
}


./seurat-4.1.0/src/RcppExports.cpp:419:    {"_Seurat_FastExpMean", (DL_FUNC) &_Seurat_FastExpMean, 2},

    {"_Seurat_FastExpMean", (DL_FUNC) &_Seurat_FastExpMean, 2},




#
# .h 头文件是函数声明
./seurat-4.1.0/src/data_manipulation.h:32:Eigen::VectorXd FastExpMean(Eigen::MatrixXd mat, bool display_progress);

Eigen::VectorXd FastExpMean(Eigen::MatrixXd mat, bool display_progress);



#
# 这个是干活的
./seurat-4.1.0/src/data_manipulation.cpp:255:Eigen::VectorXd FastExpMean(Eigen::SparseMatrix<double> mat, bool display_progress){

/* Calculates the row means of the logged values in non-log space */
// 在 非log 空间 计算 log后的值的 平均数
// [[Rcpp::export(rng = false)]]
Eigen::VectorXd FastExpMean(Eigen::SparseMatrix<double> mat, bool display_progress){
  int ncols = mat.cols();
  Eigen::VectorXd rowmeans(mat.rows());
  mat = mat.transpose(); #//转置
  if(display_progress == true){
    Rcpp::Rcerr << "Calculating gene means" << std::endl;
  }
  Progress p(mat.outerSize(), display_progress);
  for (int k=0; k<mat.outerSize(); ++k){
    p.increment();
    double rm = 0;
    for (Eigen::SparseMatrix<double>::InnerIterator it(mat,k); it; ++it){
      rm += expm1(it.value()); //exp(x)-1 后累加
    }
    rm = rm / ncols; //除以总列数，得到平均数
    rowmeans[k] = log1p(rm); //再取 log(x + 1)
  }
  return(rowmeans);
}


# R 中对应的函数
> expm1(1)
[1] 1.718282
> exp(1)-1
[1] 1.718282

#
> log1p(1)
[1] 0.6931472
> log(1+1)
[1] 0.6931472






> Seurat:::FastLogVMR
function (mat, display_progress) 
{
    .Call("_Seurat_FastLogVMR", PACKAGE = "Seurat", 
        mat, display_progress)
}
<bytecode: 0x00000000226816b0>
<environment: namespace:Seurat>








()
VariableFeatures()
./seurat-object-4.0.4/R/seurat.R:1819:VariableFeatures.Seurat <- function(

#' @rdname VariableFeatures
#' @export
#' @method VariableFeatures Seurat
#'
#' @order 7
#'
VariableFeatures.Seurat <- function(
  object,
  selection.method = NULL,
  assay = NULL,
  ...
) {
  CheckDots(...)
  object <- UpdateSlots(object = object)
  assay <- assay %||% DefaultAssay(object = object)
  return(VariableFeatures(object = object[[assay]], selection.method = selection.method))
}




#./seurat-object-4.0.4/R/assay.R:574:VariableFeatures.Assay <- function(object, selection.method = NULL, ...) {

#' @rdname VariableFeatures
#' @export
#' @method VariableFeatures Assay
#'
VariableFeatures.Assay <- function(object, selection.method = NULL, ...) {
  CheckDots(...)

  if (!is.null(x = selection.method)) {
    vf <- HVFInfo(
      object = object,
      selection.method = selection.method,
      status = TRUE
    )
    return(rownames(x = vf)[which(x = vf[, "variable"][, 1])])
  }

  return(slot(object = object, name = 'var.features'))
}

返回的就是 assay@var.features 字符串向量
> head(pbmc@assays$RNA@var.features)
[1] "PPBP"   "LYZ"    "S100A9" "IGLL5"  "GNLY"   "FTL" 











()
./seurat-4.1.0/R/visualization.R:2008:VariableFeaturePlot <- function(


#' View variable features
#'
#' @inheritParams FeatureScatter
#' @inheritParams SeuratObject::HVFInfo
#' @param cols Colors to specify non-variable/variable status
#' @param assay Assay to pull variable features from
#' @param log Plot the x-axis in log scale
#' @param raster Convert points to raster format, default is \code{NULL}
#' which will automatically use raster if the number of points plotted is greater than
#' 100,000
#'
#' @return A ggplot object
#'
#' @importFrom ggplot2 labs scale_color_manual scale_x_log10
#' @export
#' @concept visualization
#'
#' @aliases VariableGenePlot MeanVarPlot
#'
#' @seealso \code{\link{FindVariableFeatures}}
#'
#' @examples
#' data("pbmc_small")
#' VariableFeaturePlot(object = pbmc_small)
#'
VariableFeaturePlot <- function(
  object,
  cols = c('black', 'red'),
  pt.size = 1,
  log = NULL,
  selection.method = NULL,
  assay = NULL,
  raster = NULL,
  raster.dpi = c(512, 512)
) {
  # 必须提供2个颜色
  if (length(x = cols) != 2) {
    stop("'cols' must be of length 2")
  }

  # 其实返回的就是 数据框: head(pbmc@assays$RNA@meta.features[,c(1,2,4)])
  #> head(HVFInfo(pbmc, status=T))
  #                     mean    variance variance.standardized vst.variable
  #AL627309.1    0.003411676 0.003401325             0.9330441        FALSE
  #AP006222.2    0.001137225 0.001136363             0.9924937        FALSE
  hvf.info <- HVFInfo(
    object = object,
    assay = assay,
    selection.method = selection.method,
    status = TRUE
  )
  # 使用最后一列，把TF 转为0 1 +1 --> 1 2: no, yes
  # 这里炫技了。更简洁的: ifelse(hvf.info[, ncol(x = hvf.info)]=="TRUE", "yes", "no")
  var.status <- c('no', 'yes')[unlist(x = hvf.info[, ncol(x = hvf.info)]) + 1]

  # 列名第三个
  if (colnames(x = hvf.info)[3] == 'dispersion.scaled') {
    hvf.info <- hvf.info[, c(1, 2)]
  } else {
    # 是vst时刻，只保留1和3列
    hvf.info <- hvf.info[, c(1, 3)]
  }

  #坐标轴文字
  axis.labels <- switch(
    EXPR = colnames(x = hvf.info)[2],
    'variance.standardized' = c('Average Expression', 'Standardized Variance'), #vst
    'dispersion' = c('Average Expression', 'Dispersion'),
    'residual_variance' = c('Geometric Mean of Expression', 'Residual Variance')
  )
  # 是否使用log? 
  # log 肯定是函数。这样写 %||% 后面的永远不会执行吧？
  log <- log %||% (any(c('variance.standardized', 'residual_variance') %in% colnames(x = hvf.info)))
  # var.features <- VariableFeatures(object = object, assay = assay)
  # var.status <- ifelse(
  #   test = rownames(x = hvf.info) %in% var.features,
  #   yes = 'yes',
  #   no = 'no'
  # )

  # 输入2列df，开始画图 
  plot <- SingleCorPlot( #这个函数 解析 12 讲过。
    data = hvf.info,
    col.by = var.status,
    pt.size = pt.size,
    raster = raster,
    raster.dpi = raster.dpi
  )

  # 如果 var.status 只有一个 uniq 值
  if (length(x = unique(x = var.status)) == 1) {
    switch(
      EXPR = var.status[1], #既然都一样，根据第一个判断是yes or no
      'yes' = {
        cols <- cols[2]
        labels.legend <- 'Variable'
      },
      'no' = {
        cols <- cols[1]
        labels.legend <- 'Non-variable'
      }
    )
  # 图例的2个颜色
  } else {
    labels.legend <- c('Non-variable', 'Variable')
  }

  # 修饰
  plot <- plot +
    labs(title = NULL, x = axis.labels[1], y = axis.labels[2]) + #坐标轴文字
    scale_color_manual( #图例
      labels = paste(labels.legend, 'count:', table(var.status)),
      values = cols
    )
  # 对x轴取log
  if (log) {
    plot <- plot + scale_x_log10()
  }
  return(plot)
}











() LabelPoints
./seurat-4.1.0/R/visualization.R:4873:LabelPoints <- function(


#' Add text labels to a ggplot2 plot
#'
#' @param plot A ggplot2 plot with a GeomPoint layer
#' @param points A vector of points to label; if \code{NULL}, will use all points in the plot
#' @param labels A vector of labels for the points; if \code{NULL}, will use
#' rownames of the data provided to the plot at the points selected
#' @param repel Use \code{geom_text_repel} to create a nicely-repelled labels; this
#' is slow when a lot of points are being plotted. If using \code{repel}, set \code{xnudge}
#' and \code{ynudge} to 0
#' @param xnudge,ynudge Amount to nudge X and Y coordinates of labels by
#' @param ... Extra parameters passed to \code{geom_text}
#'
#' @return A ggplot object
#'
#' @importFrom ggrepel geom_text_repel
#' @importFrom ggplot2 geom_text aes_string
#' @export
#' @concept visualization
#'
#' @aliases Labeler
#' @seealso \code{\link[ggplot2]{geom_text}}
#'
#' @examples
#' data("pbmc_small")
#' ff <- TopFeatures(object = pbmc_small[['pca']])
#' cc <- TopCells(object = pbmc_small[['pca']])
#' plot <- FeatureScatter(object = pbmc_small, feature1 = ff[1], feature2 = ff[2])
#' LabelPoints(plot = plot, points = cc)
#'
LabelPoints <- function(
  plot,
  points,
  labels = NULL,
  repel = FALSE,
  xnudge = 0.3,
  ynudge = 0.05,
  ...
) {
  xynames <- GetXYAesthetics(plot = plot) #获取 x、y轴散点图的数据列名字，是一个list

  # 点的名字，就是基因名
  # 如果没有，则使用 数据的行名，就是所有基因名。
  # ggplot2的数据是存储在 g1$data 中的 
  points <- points %||% rownames(x = plot$data) 

  # 如果 points 是数字，则转为基因名
  if (is.numeric(x = points)) {
    points <- rownames(x = plot$data) #错误：这样是取全部基因名
    #points <- rownames(x = plot$data)[points] #更正
    # pr: https://github.com/satijalab/seurat/pull/5727
  }

  # 求交集：输入点和
  points <- intersect(x = points, y = rownames(x = plot$data))

  # 如果交集是0，报错
  if (length(x = points) == 0) {
    stop("Cannot find points provided")
  }

  # 如果没有labels，就使用 基因名
  labels <- labels %||% points
  # 转为字符串
  labels <- as.character(x = labels)


  # 按这几个基因，取数据子集
  label.data <- plot$data[points, ]
  # 添加labels列
  label.data$labels <- labels


  # 是否使用 repel，决定使用哪个函数
  geom.use <- ifelse(test = repel, yes = geom_text_repel, no = geom_text)

  # 如果互斥，则提示。
  if (repel) {
    if (!all(c(xnudge, ynudge) == 0)) { #??
      message("When using repel, set xnudge and ynudge to 0 for optimal results")
    }
  }

  # 添加一层: 文字标签
  plot <- plot + geom.use(
    mapping = aes_string(x = xynames$x, y = xynames$y, label = 'labels'),
    data = label.data,
    nudge_x = xnudge, #nudge [nʌdʒ] v. 轻推移动，推开
    nudge_y = ynudge,
    ...
  )

  return(plot)
}












() GetXYAesthetics 获取一个ggplot2散点图的x、y数据的列名!
还真有这操作！

$ find . | xargs grep -n "GetXYAesthetics" --color=auto 2>/dev/null
./seurat-4.1.0/R/visualization.R:5805:GetXYAesthetics <- function(plot, geom = 'GeomPoint', plot.first = TRUE) {



# Get X and Y aesthetics from a plot for a certain geom
#
# @param plot A ggplot2 object
# @param geom Geom class to filter to
# @param plot.first Use plot-wide X/Y aesthetics before geom-specific aesthetics
#
# @return A named list with values 'x' for the name of the x aesthetic and 'y' for the y aesthetic
#
#' @importFrom rlang as_label
#
GetXYAesthetics <- function(plot, geom = 'GeomPoint', plot.first = TRUE) {
  # 获取 每一层的 geom的类名
  geoms <- sapply(
    X = plot$layers,
    FUN = function(layer) {
      return(class(x = layer$geom)[1])
    }
  )

  # handle case where raster is set to True
  # 矫正类型：栅格化的
  if (geom == "GeomPoint" && "GeomScattermore" %in% geoms){
    geom <- "GeomScattermore"
  }

  # 哪个等于输入参数，返回的是一系列数字下标
  geoms <- which(x = geoms == geom)

  # 如果都不等于输入参数，则报错
  if (length(x = geoms) == 0) {
    stop("Cannot find a geom of class ", geom)
  }

  #取最小的数字下标
  geoms <- min(geoms)

  if (plot.first) {
    # x <- as.character(x = plot$mapping$x %||% plot$layers[[geoms]]$mapping$x)[2]
    x <- as_label(x = plot$mapping$x %||% plot$layers[[geoms]]$mapping$x)
    # y <- as.character(x = plot$mapping$y %||% plot$layers[[geoms]]$mapping$y)[2]
    y <- as_label(x = plot$mapping$y %||% plot$layers[[geoms]]$mapping$y)
  } else {
    x <- as_label(x = plot$layers[[geoms]]$mapping$x %||% plot$mapping$x)
    y <- as_label(x = plot$layers[[geoms]]$mapping$y %||% plot$mapping$y)
  }

  return(list('x' = x, 'y' = y))
}

